﻿/*******************************************************
 * 
 * 作者：胡庆访
 * 创建时间：20100331
 * 说明：延迟加载外键实体 的实现
 * 运行环境：.NET 3.5 SP1
 * 版本号：1.0.0
 * 
 * 历史记录：
 * 创建文件 胡庆访 20100331
 * 
*******************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections;

namespace OpenExpressApp
{
    /// <summary>
    /// 外键的拥有者
    /// </summary>
    internal interface IReferenceOwner
    {
        /// <summary>
        /// 当外键改变时，可以回调Owner，通知它Id属性已经改变。
        /// </summary>
        /// <param name="propertyName"></param>
        void NotifyIdChanged(Type refType);

        /// <summary>
        /// 当外键改变时，可以回调Owner，通知它外键引用的实体属性已经改变。
        /// </summary>
        /// <param name="propertyName"></param>
        void NotifyEntityChanged(Type refType);
    }

    /// <summary>
    /// 延迟加载的引用实体
    /// （外键等）
    /// </summary>
    /// <typeparam name="TModel"></typeparam>
    /// <typeparam name="TKey"></typeparam>
    public interface ILazyEntityRef<TEntity> : ICloneable<ILazyEntityRef<TEntity>>
        where TEntity : Entity<TEntity>
    {
        /// <summary>
        /// 引用对象的键
        /// </summary>
        Guid Id { get; set; }

        /// <summary>
        /// 此属性综合了Id和IsEmpty属性。
        /// </summary>
        Guid? NullableId { get; set; }

        /// <summary>
        /// 被引用的对象
        /// </summary>
        TEntity Entity { get; set; }

        /// <summary>
        /// 是否已经加载/设置了实体引用对象
        /// </summary>
        bool LoadedOrAssigned { get; }

        /// <summary>
        /// 表示是否已经清空这个引用，不再引用任何实体。
        /// </summary>
        bool IsEmpty { get; }

        /// <summary>
        /// 直接加载外键实体。
        /// 
        /// 如果list中有指定的实体，则直接加载。
        /// 否则，从存储层加载
        /// </summary>
        /// <param name="list"></param>
        void Load(IList list);
    }

    /// <summary>
    /// 延迟加载的外键
    /// </summary>
    /// <typeparam name="TEntity"></typeparam>
    [Serializable]
    internal class LazyEntityRef<TEntity> : ILazyEntityRef<TEntity>
        where TEntity : Entity<TEntity>
    {
        private Guid _id;

        private TEntity _entity;

        private Func<Guid, TEntity> _loader;

        /// <summary>
        /// 外键的拥有者
        /// </summary>
        private IReferenceOwner _owner;

        private bool _notifyRefEntityChanged;

        public LazyEntityRef(Func<Guid, TEntity> loader, IReferenceOwner owner, bool notifyRefEntityChanged)
        {
            Debug.Assert(loader != null, "没有注明加载器！");

            this._id = default(Guid);
            this._loader = loader;
            this._owner = owner;
            this._notifyRefEntityChanged = notifyRefEntityChanged;
        }

        /// <summary>
        /// 被引用实体的ID
        /// </summary>
        public Guid Id
        {
            get
            {
                return this._id;
            }
            set
            {
                if (this._id.Equals(value) == false)
                {
                    this._id = value;

                    //这里为了保证和原系统的吻合，暂时去除
                    //this._entity = null;

                    this.OnIdChanged();
                }
            }
        }

        public Guid? NullableId
        {
            get
            {
                if (this.IsEmpty)
                {
                    return null;
                }
                return this.Id;
            }
            set
            {
                this.Id = value.HasValue ? value.Value : default(Guid);
            }
        }

        /// <summary>
        /// 这个值用于判断是否正在处于设置Entity的状态中。
        /// 
        /// 当在外界设置Entity属性时，如果获取Entity属性，不需要引起延迟加载。
        /// </summary>
        private bool _settingEntity;

        /// <summary>
        /// 如果是第一次获取此属性，并且还没有为它赋值时，会自动根据Id查询出对应的Entity。
        /// </summary>
        public TEntity Entity
        {
            get
            {
                if (this._settingEntity == false && this._entity == null)
                {
                    this.LoadEntity();
                }

                return this._entity;
            }
            set
            {
                var oldEntity = this._entity;

                try
                {
                    this._settingEntity = true;

                    this._entity = value;

                    //这里为了保证和原系统的吻合，暂时去除
                    //if (value == null)
                    //{
                    //    this._id = default(Guid);
                    //}
                    //else
                    //{
                    //    this._id = value.Id;
                    //}

                    if (oldEntity != value)
                    {
                        this.OnEntityChanged();
                    }
                }
                finally
                {
                    this._settingEntity = false;
                }
            }
        }

        public bool IsEmpty
        {
            get
            {
                return this._id.Equals(default(Guid));
            }
        }

        public bool LoadedOrAssigned
        {
            get
            {
                return this._entity != null;
            }
        }

        private void OnIdChanged()
        {
            this._owner.NotifyIdChanged(typeof(TEntity));
        }

        private void OnEntityChanged()
        {
            if (this._notifyRefEntityChanged)
            {
                this._owner.NotifyEntityChanged(typeof(TEntity));
            }
        }

        private void LoadEntity()
        {
            if (this.IsEmpty)
            {
                return;
            }

            this._entity = this._loader(this._id);
        }

        private void Clone(LazyEntityRef<TEntity> target)
        {
            this._id = target._id;
            this._entity = target._entity;
            this._loader = target._loader;
            //this._owner = target._owner;
        }

        #region ICloneable<ILazyEntityRef<TEntity>> Members

        void ICloneable<ILazyEntityRef<TEntity>>.Clone(ILazyEntityRef<TEntity> target)
        {
            var targetRef = target as LazyEntityRef<TEntity>;
            if (targetRef != null)
            {
                this.Clone(targetRef);
            }
        }

        #endregion

        public void Load(IList list)
        {
            if (this.IsEmpty == false)
            {
                //尝试从缓存中读取。
                var fkEntity = list.OfType<TEntity>().FirstOrDefault(e => e.Id == this._id);
                if (fkEntity != null)
                {
                    this.Entity = fkEntity;
                }
                else
                {
                    //缓存中没有，则从数据层加载
                    fkEntity = this.Entity;

                    //加载完毕，加入链表。
                    if (fkEntity != null &&
                        list.IsReadOnly == false && list.IsFixedSize == false
                        )
                    {
                        list.Add(fkEntity);
                    }
                }
            }
        }

        public override string ToString()
        {
            if (this.IsEmpty)
            {
                return "Empty";
            }
            if (this.LoadedOrAssigned)
            {
                return this._entity.ToString();
            }
            return "NotLoaded:" + this._id;
        }
    }
}
